#include"C++.h"

//类的定义
//类定义格式 - class为定义类的关键字，Stack为类的名字，{}中为类的主体，注意类定义结束时后⾯分号不能省略
//类体中内容称为类的成员：类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数
//C++中struct也可以定义类，C++兼容C中struct的⽤法，同时struct升级成了类，明显的变化是struct中可以定义函数，⼀般情况下我们还是推荐⽤class定义类
//定义在类⾯的成员函数默认为inline。

//访问限定符 - C++⼀种实现封装的⽅式，⽤类将对象的属性与⽅法结合在⼀块，让对象更加完善，通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤
//public修饰的成员在类外可以直接被访问；protected和private修饰的成员在类外不能直接被访问
//访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌，如果后⾯没有访问限定符，作⽤域就到 }即类结束。
//class定义成员没有被访问限定符修饰时默认为private，struct默认为public。
//⼀般成员变量都会被限制为private/protected，需要给别⼈使⽤的成员函数会放为public
//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//private:
//	// 为了区分成员变量，⼀般习惯上成员变量
//	// 会加⼀个特殊标识，如_ 或者 m开头
//	int _year; // year_ m_year
//	int _month;
//	int _day;
//};
//int main()
//{
//	Date d;
//	d.Init(2024, 3, 31);
//	return 0;
//}


//类域 - 类定义了⼀个新的作⽤域，类的所有成员都在类的作⽤域中，在类体外定义成员时，需要使⽤ :: 作⽤域操作符指明成员属于哪个类域


//实例化 - ⽤类类型在物理内存中创建对象的过程，称为类实例化出对象
//类是对象进⾏⼀种抽象描述，是⼀个模型⼀样的东西，限定了类有哪些成员变量，这些成员变量只是声明，没有分配空间，⽤类实例化出对象时，才会分配空间

//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	void Print()
//	{
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//private:
//	// 这⾥只是声明，没有开空间
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	// Date类实例化出对象d1和d2
//	Date d1;
//	Date d2;
//	d1.Init(2024, 3, 31);
//	d1.Print();
//	d2.Init(2024, 7, 5);
//	d2.Print();
//	return 0;
//}

//内存对⻬规则
//第⼀个成员在与结构体偏移量为0的地址处。
//其他成员变量要对⻬到某个数字（对⻬数）的整数倍的地址处。
//注意：对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。
//VS中默认的对⻬数为8
//结构体总⼤⼩为：最⼤对⻬数（所有变量类型最⼤者与默认对⻬参数取最⼩）的整数倍。
//如果嵌套了结构体的情况，嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处，结构体的整体⼤⼩就是所有最⼤对⻬数（含嵌套结构体的对⻬数）的整数倍。
//如果类没有成员变量，为了占位标识对象存在，所以给1字节大小的空间
//class A
//{
//public:
//	void Print()
//	{
//		cout << _ch << endl;
//	}
//private:
//	char _ch;
//	int _i;
//};
//class B
//{
//public:
//	void Print()
//	{
//		//...
//	}
//};
//class C
//{};
//int main()
//{
//	A a;
//	B b;
//	C c;
//	cout << sizeof(a) << endl;
//	cout << sizeof(b) << endl;
//	cout << sizeof(c) << endl;
//	return 0;
//}


//this指针
//编译器编译后，类的成员函数默认都会在形参第⼀个位置，增加⼀个当前类类型的指针，叫做this指针。
//⽐如Date类的Init的真实原型为， void Init(Date* const this, int year,int month, int day)
//类的成员函数中访问成员变量，本质都是通过this指针访问的
//C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理)，但是可以在函数体内显⽰使⽤this指针
//class Date
//{
//public:
//	// void Init(Date* const this, int year, int month, int day)
//	void Init(int year, int month, int day)
//	{
//		// 编译报错：error C2106: “=”: 左操作数必须为左值
//		// this = nullptr;
//		// this->_year = year;
//		_year = year;
//		this->_month = month;
//		this->_day = day;
//	}
//	void Print()
//	{
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//private:
//	// 这⾥只是声明，没有开空间
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	// Date类实例化出对象d1和d2
//	Date d1;
//	Date d2;
//	// d1.Init(&d1, 2024, 3, 31);
//	d1.Init(2024, 3, 31);
//	d1.Print();
//	d2.Init(2024, 7, 5);
//	d2.Print();
//	return 0;
//}


//类的默认成员函数 - 默认成员函数就是⽤⼾没有显式实现，编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类，我们不写的情况下编译器会默认⽣成以下6个默认成员函数
//初始化 - 构造函数，清理- 析构函数，拷贝复制 - 拷贝构造，赋值重载，取地址重载 - 普通对象，const对象取地址

//构造函数 - 特殊的成员函数，对象实例化时初始化对象
//特点：1. 函数名与类名相同。
//2. ⽆返回值。(返回值啥都不需要给，也不需要写void，不要纠结，C++规定如此)
//3. 对象实例化时系统会⾃动调⽤对应的构造函数。
//4. 构造函数可以重载。
//5. 如果类中没有显式定义构造函数，则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数，⼀旦⽤⼾显式定义编译器将不再⽣成。
//6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数，都叫做默认构造函数。但是这三个函数有且只有⼀个存在，不能同时存在。不传实参就可以调⽤的构造就叫默认构造。
//7. 我们不写，编译器默认⽣成的构造，对内置类型成员变量的初始化没有要求，也就是说是是否初始化是不确定的，看编译器。对于⾃定义类型成员变量，要求调⽤这个成员变量的默认构造函数初始化
//C++把类型分成内置类型(基本类型)和⾃定义类型。内置类型就是语⾔提供的原⽣数据类型，
//如：int / char / double / 指针等，⾃定义类型就是我们使⽤class / struct等关键字⾃⼰定义的类型。
//class Date
//{
//public:
//	// 1.⽆参构造函数
//	//Date()
//	//{
//	//	_year = 1;
//	//	_month = 1;
//	//	_day = 1;
//	//}
//	// 2.带参构造函数
//	//Date(int year, int month, int day)
//	//{
//	//	_year = year;
//	//	_month = month;
//	//	_day = day;
//	//}
//	// 3.全缺省构造函数
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//	_year = year;
//	_month = month;
//	_day = day;
//	}
//	void Print()
//	{
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	// 如果留下三个构造中的第⼆个带参构造，第⼀个和第三个注释掉
//	// 编译报错：error C2512: “Date”: 没有合适的默认构造函数可⽤
//	Date d1; // 调⽤默认构造函数
//	Date d2(2025, 1, 1); // 调⽤带参的构造函数
//	// 注意：如果通过⽆参构造函数创建对象时，对象后⾯不⽤跟括号，否则编译器⽆法
//	// 区分这⾥是函数声明还是实例化对象
//	// warning C4930: “Date d3(void)”: 未调⽤原型函数(是否是有意⽤变量定义的?)
//	Date d3();
//	d1.Print();
//	d2.Print();
//	return 0;
//}


//析构函数 - 完成对象中资源的清理释放⼯作
//1. 析构函数名是在类名前加上字符 ~。
//2. ⽆参数⽆返回值。(这⾥跟构造类似，也不需要加void)
//3. ⼀个类只能有⼀个析构函数。若未显式定义，系统会⾃动⽣成默认的析构函数。
//4. 对象⽣命周期结束时，系统会⾃动调⽤析构函数。
//5. 跟构造函数类似，我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理，⾃定类型成员会调⽤他的析构函数。
//6. 还需要注意的是我们显⽰写析构函数，对于⾃定义类型成员也会调⽤他的析构，也就是说⾃定义类型成员⽆论什么情况都会⾃动调⽤析构函数。 
//7. 如果类中没有申请资源时，析构函数可以不写，直接使⽤编译器⽣成的默认析构函数，如Date；如果默认⽣成的析构就可以⽤，也就不需要显⽰写析构，如MyQueue；但是有资源申请时，⼀定要⾃⼰写析构，否则会造成资源泄漏，如Stack。
//8. ⼀个局部域的多个对象，C++规定后定义的先析构
//typedef int STDataType;
//class Stack
//{
//public:
//	Stack(int n = 4)
//	{
//		_a = (STDataType*)malloc(sizeof(STDataType) * n);
//		if (nullptr == _a)
//		{
//			perror("malloc申请空间失败");
//			return;
//		}
//		_capacity = n;
//		_top = 0;
//	}
//	~Stack()
//	{
//		cout << "~Stack()" << endl;
//		free(_a);
//		_a = nullptr;
//		_top = _capacity = 0;
//	}
//private:
//	STDataType* _a;
//	size_t _capacity;
//	size_t _top;
//};
//// 两个Stack实现队列
//class MyQueue
//{
//public:
//	//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构，释放的Stack内部的资源
//	// 显⽰写析构，也会⾃动调⽤Stack的析构
///*~MyQueue()
//{}*/
//private:
//	Stack pushst;
//	Stack popst;
//};
//int main()
//{
//	Stack st;
//	MyQueue mq;
//	return 0;
//}


//拷⻉构造函数 - 如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤，且任何额外的参数都有默认值，则此构造函数也叫做拷⻉构造函数，也就是说拷⻉构造是⼀个特殊的构造函数
//1. 拷⻉构造函数是构造函数的⼀个重载。
//2. 拷⻉构造函数的参数只有⼀个且必须是类类型对象的引⽤，使⽤传值⽅式编译器直接报错，因为语法逻辑上会引发⽆穷递归调⽤。
//3. C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造，所以这⾥⾃定义类型传值传参和传值返回都会调⽤拷⻉构造完成。
//4. 若未显式定义拷⻉构造，编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成员变量会完成值拷⻉ / 浅拷⻉(⼀个字节⼀个字节的拷⻉)，对⾃定义类型成员变量会调⽤他的拷⻉构造。
//5. 类成员变量全是内置类型且没有指向什么资源，编译器⾃动⽣成的拷⻉构造就可以完成需要的拷⻉，所以不需要我们显⽰实现拷⻉构造
//   像Stack这样的类，虽然也都是内置类型，但是_a指向了资源，编译器⾃动⽣成的拷⻉构造完成的值拷⻉ / 浅拷⻉不符合我们的需求，所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)
//   像MyQueue这样的类型内部主要是⾃定义类型Stack成员，编译器⾃动⽣成的拷⻉构造会调⽤Stack的拷⻉构造，也不需要我们显⽰实现MyQueue的拷⻉构造
//   如果⼀个类显⽰实现了析构并释放资源，那么他就需要显⽰写拷⻉构造，否则就不需要
//6. 传引⽤返回可以减少拷⻉，但是⼀定要确保返回对象，在当前函数结束后还在，才能⽤引⽤返回
//class Date
//{
//public:
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	// 编译报错：error C2652: “Date”: ⾮法的复制构造函数: 第⼀个参数不应是“Date”
//	//Date(Date d)
//	Date(const Date& d)
//	{
//		_year = d._year;
//		_month = d._month;
//		_day = d._day;
//	}
//	Date(Date* d)
//	{
//		_year = d->_year;
//		_month = d->_month;
//		_day = d->_day;
//	}
//	void Print()
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//void Func1(Date d)
//{
//	cout << &d << endl;
//	d.Print();
//}
//// Date Func2()
//Date& Func2()
//{
//	Date tmp(2024, 7, 5);
//	tmp.Print();
//	return tmp;
//}
//int main()
//{
//	Date d1(2024, 7, 5);
//	// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造，所以这⾥传值传参要调⽤拷⻉构造
//		// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉，传引⽤传参可以较少这⾥的拷⻉
//		Func1(d1);
//	cout << &d1 << endl;
//	// 这⾥可以完成拷⻉，但是不是拷⻉构造，只是⼀个普通的构造
//	Date d2(&d1);
//	d1.Print();
//	d2.Print();
//
//	//这样写才是拷⻉构造，通过同类型的对象初始化构造，⽽不是指针
//	Date d3(d1);
//	d2.Print(); 
//
//	// 也可以这样写，这⾥也是拷⻉构造
//	Date d4 = d1;
//	d2.Print();
//
//	// Func2返回了⼀个局部对象tmp的引⽤作为返回值
//	// Func2函数结束，tmp对象就销毁了，相当于了⼀个野引⽤
//	Date ret = Func2();
//	ret.Print();
//	return 0;
//}


//运算符重载 - 当运算符被⽤于类类型的对象时，C++语⾔允许我们通过运算符重载的形式指定新的含义。
// C++规定类类型对象使⽤运算符时，必须转换成调⽤对应运算符重载，若没有对应的运算符重载，则会编译报错
// 1.运算符重载是具有特殊名字的函数，他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样，它也具有其返回类型和参数列表以及函数体。
//重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数，⼆元运算符有两个参数，⼆元运算符的左侧运算对象传给第⼀个参数，右侧运算对象传给第⼆个参数。
// 2.如果⼀个重载运算符函数是成员函数，则它的第⼀个运算对象默认传给隐式的this指针，因此运算符重载作为成员函数时，参数⽐运算对象少⼀个。
// 3.运算符重载以后，其优先级和结合性与对应的内置类型运算符保持⼀致。
// 4.不能通过连接语法中没有的符号来创建新的操作符：⽐如operator@
// 5. .* :: sizeof ? : .注意以上5个运算符不能重载。
// 6.重载操作符⾄少有⼀个类类型参数，不能通过运算符重载改变内置类型对象的含义，如： intoperator+(int x, int y)
// 7.⼀个类需要重载哪些运算符，是看哪些运算符重载后有意义，⽐如Date类重载operator - 就有意义，但是重载operator + 就没有意义。
// 8.C++规定，后置++重载时，增加⼀个int形参，跟前置++构成函数重载，⽅便区分
// 9.重载<<和>>时，需要重载为全局函数，因为重载为成员函数，this指针默认抢占了第⼀个形参位置，
//   第⼀个形参位置是左侧运算对象，调⽤时就变成了 对象 << cout，不符合使⽤习惯和可读性
//   重载为全局函数把ostream/istream放到第⼀个形参位置就可以了，第⼆个形参位置当类类型对象。
// 
// 编译报错：“operator +”必须⾄少有⼀个类类型的形参
//int operator+(int x, int y)
//{
//	return x - y;
//}
//class A
//{
//public:
//	void func()
//	{
//		cout << "A::func()" << endl;
//	}
//};
//typedef void(A::* PF)(); //成员函数指针类型
//int main()
//{
//	// C++规定成员函数要加&才能取到函数指针
//	PF pf = &A::func;
//
//	A obj;//定义ob类对象temp
//
//	// 对象调⽤成员函数指针时，使⽤.*运算符
//	(obj.*pf)();
//
//	return 0;
//}

//class Date
//{
//public:
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	void Print()
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//	//private:
//	int _year;
//	int _month;
//	int _day;
//};
//// 重载为全局的⾯临对象访问私有成员变量的问题
//// 有⼏种⽅法可以解决：
//// 1、成员放公有
//// 2、Date提供getxxx函数
//// 3、友元函数
//// 4、重载为成员函数
//bool operator==(const Date& d1, const Date& d2)
//{
//	return d1._year == d2._year
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}
//int main()
//{
//	Date d1(2024, 7, 5);
//	Date d2(2024, 7, 6);
//	// 运算符重载函数可以显⽰调⽤
//	operator==(d1, d2);
//	// 编译器会转换成 operator==(d1, d2);
//	d1 == d2;
//	return 0;
//}

//class Date
//{
//public:
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	void Print()
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//	bool operator==(const Date& d)
//	{
//		return _year == d._year
//			&& _month == d._month
//			&& _day == d._day;
//	}
//	Date& operator++()
//	{
//		cout << "前置++" << endl;
//		//...
//		return *this;
//	}
//	Date operator++(int)
//	{
//		Date tmp;
//		cout << "后置++" << endl;
//		//...
//		return tmp;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	Date d1(2024, 7, 5);
//	Date d2(2024, 7, 6);
//	// 运算符重载函数可以显⽰调⽤
//	d1.operator==(d2);
//	// 编译器会转换成 d1.operator==(d2);
//	d1 == d2;
//	// 编译器会转换成 d1.operator++();
//	++d1;
//	// 编译器会转换成 d1.operator++(0);
//	d1++;
//	return 0;
//}


//赋值运算符重载 - 赋值运算符重载是⼀个默认成员函数，⽤于完成两个已经存在的对象直接的拷⻉赋值
//1. 赋值运算符重载是⼀个运算符重载，规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引⽤，否则会传值传参会有拷⻉
//2. 有返回值，且建议写成当前类类型引⽤，引⽤返回可以提⾼效率，有返回值⽬的是为了⽀持连续赋值场景
//3. 没有显式实现时，编译器会⾃动⽣成⼀个默认赋值运算符重载，默认赋值运算符重载⾏为跟默认拷贝构造函数类似，
//	 对内置类型成员变量会完成值拷⻉ / 浅拷⻉(⼀个字节⼀个字节的拷⻉)，对⾃定义类成员变量会调⽤他的复制重载。
//4. 如果⼀个类显⽰实现了析构并释放资源，那么他就需要显⽰写赋值运算符重载，否则就不需要
//class Date
//{
//public:
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	Date(const Date& d)
//	{
//		cout << " Date(const Date& d)" << endl;
//		_year = d._year;
//		_month = d._month;
//		_day = d._day;
//	}
//	// 传引⽤返回减少拷⻉
//	// d1 = d2;
//	Date& operator=(const Date& d)
//	{
//		// 不要检查⾃⼰给⾃⼰赋值的情况
//		if (this != &d)
//		{
//			_year = d._year;
//			_month = d._month;
//			_day = d._day;
//		}
//		// d1 = d2表达式的返回对象应该为d1，也就是*this
//		return *this;
//	}
//	void Print()
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	Date d1(2024, 7, 5);
//	Date d2(d1);
//	Date d3(2024, 7, 6);
//	d1 = d3;
//	// 需要注意这⾥是拷⻉构造，不是赋值重载
//	// 请牢牢记住赋值重载完成两个已经存在的对象直接的拷⻉赋值
//	// ⽽拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象
//	Date d4 = d1;
//	return 0;
//}

//Date.h
//class Date
//{
//	// 友元函数声明
//	friend ostream& operator<<(ostream& out, const Date& d);
//	friend istream& operator>>(istream& in, Date& d);
//public:
//	Date(int year = 1900, int month = 1, int day = 1);
//	void Print() const;
//	// 直接定义类⾥⾯，他默认是inline
//	// 频繁调⽤
//	int GetMonthDay(int year, int month)
//	{
//		assert(month > 0 && month < 13);
//		static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,
//		31, 31, 30, 31, 30, 31 };
//		// 365天 5h +
//		if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year
//			% 400 == 0))
//		{
//			return 29;
//		}
//		else
//		{
//			return monthDayArray[month];
//		}
//	}
//	bool CheckDate();
//	bool operator<(const Date& d) const;
//	bool operator<=(const Date& d) const;
//	bool operator>(const Date& d) const;
//	bool operator>=(const Date& d) const;
//	bool operator==(const Date& d) const;
//	bool operator!=(const Date& d) const;
//	// d1 += 天数
//	Date& operator+=(int day);
//	Date operator+(int day) const;
//	// d1 -= 天数
//	Date& operator-=(int day);
//	Date operator-(int day) const;
//	// d1 - d2
//	int operator-(const Date& d) const;
//	// ++d1 -> d1.operator++()
//	Date& operator++();
//	// d1++ -> d1.operator++(0)
//	// 为了区分，构成重载，给后置++，强⾏增加了⼀个int形参
//	// 这⾥不需要写形参名，因为接收值是多少不重要，也不需要⽤
//	// 这个参数仅仅是为了跟前置++构成重载区分
//	Date operator++(int);
//	Date& operator--();
//	Date operator--(int);
//	// 流插⼊
//	// 不建议，因为Date* this占据了⼀个参数位置，使⽤d<<cout不符合习惯
//	//void operator<<(ostream& out);
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//// 重载
//ostream& operator<<(ostream& out, const Date& d);
//istream& operator>>(istream& in, Date& d);
//// Date.cpp
//bool Date::CheckDate()
//{
//	if (_month < 1 || _month > 12
//		|| _day < 1 || _day > GetMonthDay(_year, _month))
//	{
//		return false;
//	}
//	else
//	{
//		return true;
//	}
//}
//Date::Date(int year, int month, int day)
//{
//	_year = year;
//	_month = month;
//	_day = day;
//	if (!CheckDate())
//	{
//		cout << "⽇期⾮法" << endl;
//	}
//}
//void Date::Print() const
//{
//	cout << _year << "-" << _month << "-" << _day << endl;
//}
//// d1 < d2
//bool Date::operator<(const Date& d) const
//{
//	if (_year < d._year)
//	{
//		return true;
//	}
//	else if (_year == d._year)
//	{
//		if (_month < d._month)
//		{
//			return true;
//		}
//		else if (_month == d._month)
//		{
//			return _day < d._day;
//		}
//	}
//	return false;
//}
//// d1 <= d2
//bool Date::operator<=(const Date& d) const
//{
//	return *this < d || *this == d;
//}
//bool Date::operator>(const Date& d) const
//{
//	return !(*this <= d);
//}
//bool Date::operator>=(const Date& d) const
//{
//	return !(*this < d);
//}
//bool Date::operator==(const Date& d) const
//{
//	return _year == d._year
//		&& _month == d._month
//		&& _day == d._day;
//}
//bool Date::operator!=(const Date& d) const
//{
//	return !(*this == d);
//}
//// d1 += 50
//// d1 += -50
//Date& Date::operator+=(int day)
//{
//	if (day < 0)
//	{
//		return *this -= -day;
//	}
//	_day += day;
//	while (_day > GetMonthDay(_year, _month))
//	{
//		_day -= GetMonthDay(_year, _month);
//		++_month;
//		if (_month == 13)
//		{
//			++_year;
//			_month = 1;
//		}
//	}
//	return *this;
//}
//Date Date::operator+(int day) const
//{
//	Date tmp = *this;
//	tmp += day;
//	return tmp;
//}
//// d1 -= 100
//Date& Date::operator-=(int day)
//{
//	if (day < 0)
//	{
//		return *this += -day;
//	}
//	_day -= day;
//	while (_day <= 0)
//	{
//		--_month;
//		if (_month == 0)
//		{
//			_month = 12;
//			_year--;
//		}
//		// 借上⼀个⽉的天数
//		_day += GetMonthDay(_year, _month);
//	}
//	return *this;
//}
//Date Date::operator-(int day) const
//{
//	Date tmp = *this;
//	tmp -= day;
//	return tmp;
//}
////++d1
//Date& Date::operator++()
//{
//	*this += 1;
//	return *this;
//}
//// d1++
//Date Date::operator++(int)
//{
//	Date tmp(*this);
//	*this += 1;
//	return tmp;
//}
//Date& Date::operator--()
//{
//	*this -= 1;
//	return *this;
//}
//Date Date::operator--(int)
//{
//	Date tmp = *this;
//	*this -= 1;
//	return tmp;
//}
//// d1 - d2
//int Date::operator-(const Date& d) const
//{
//	Date max = *this;
//	Date min = d;
//	int flag = 1;
//	if (*this < d)
//	{
//		max = d;
//		min = *this;
//		flag = -1;
//	}
//	int n = 0;
//	while (min != max)
//	{
//		++min;
//		++n;
//	}
//	return n * flag;
//}
//ostream& operator<<(ostream & out, const Date & d)
//{
//	out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;
//	return out;
//}
//istream& operator>>(istream & in, Date & d)
//{
//	cout << "请依次输⼊年⽉⽇:>";
//	in >> d._year >> d._month >> d._day;
//	if (!d.CheckDate())
//	{
//		cout << "⽇期⾮法" << endl;
//	}
//	return in;
//}
//// Test.cpp
//void TestDate1()
//{
//	// 这⾥需要测试⼀下⼤的数据+和-
//	Date d1(2024, 4, 14);
//	Date d2 = d1 + 30000;
//	d1.Print();
//	d2.Print();
//	Date d3(2024, 4, 14);
//	Date d4 = d3 - 5000;
//	d3.Print();
//	d4.Print();
//	Date d5(2024, 4, 14);
//	d5 += -5000;
//	d5.Print();
//}
//void TestDate2()
//{
//	Date d1(2024, 4, 14);
//	Date d2 = ++d1;
//	d1.Print();
//	d2.Print();
//	Date d3 = d1++;
//	d1.Print();
//	d3.Print();
//	/*d1.operator++(1);
//	d1.operator++(100);
//	d1.operator++(0);
//	d1.Print();*/
//}
//void TestDate3()
//{
//	Date d1(2024, 4, 14);
//	Date d2(2034, 4, 14);
//	int n = d1 - d2;
//	cout << n << endl;
//	n = d2 - d1;
//}
//void TestDate4()
//{
//	Date d1(2024, 4, 14);
//	Date d2 = d1 + 30000;
//	// operator<<(cout, d1)
//	cout << d1;
//	cout << d2;
//	cin >> d1 >> d2;
//	cout << d1 << d2;
//}
//void TestDate5()
//{
//	const Date d1(2024, 4, 14);
//	d1.Print();
//	//d1 += 100;
//	d1 + 100;
//	Date d2(2024, 4, 25);
//	d2.Print();
//	d2 += 100;
//	d1 < d2;
//	d2 < d1;
//}
//int main()
//{
//
//	return 0;
//}


//取地址运算符重载
//const成员函数 - 将const修饰的成员函数称之为const成员函数，const修饰成员函数放到成员函数参数列表的后⾯
//const实际修饰该成员函数隐含的this指针，表明在该成员函数中不能对类的任何成员进⾏修改
//const 修饰Date类的Print成员函数，Print隐含的this指针由 Date* const this 变为 constDate* const this
//class Date
//{
//public:
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	// void Print(const Date* const this) const
//	void Print() const
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	// 这⾥⾮const对象也可以调⽤const成员函数是⼀种权限的缩⼩
//	Date d1(2024, 7, 5);
//	d1.Print();
//	const Date d2(2024, 8, 5);
//	d2.Print();
//	return 0;
//}

//取地址运算符重载
//class Date
//{
//public:
//	Date* operator&()
//	{
//		return this;
//		// return nullptr;
//	}
//	const Date* operator&()const
//		 {
//		 return this;
//		 // return nullptr;
//			 }
// private:
//	 int _year; // 年
//	 int _month; // ⽉
//	 int _day; // ⽇
//	
//};


//构造函数
//•	实现构造函数时，初始化成员变量主要使⽤函数体内赋值，构造函数初始化还有⼀种⽅式，就是初始化列表。
//	初始化列表的使⽤⽅式是以⼀个冒号开始，接着是⼀个以逗号分隔的数据成员列表，每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式
//• 每个成员变量在初始化列表中只能出现⼀次，语法理解上初始化列表可以认为是每个成员变量定义初始化的地⽅。
//• 引⽤成员变量，const成员变量，没有默认构造的类类型变量，必须放在初始化列表位置进⾏初始化，否则会编译报错
//• C++11⽀持在成员变量声明的位置给缺省值，这个缺省值主要是给没有显⽰在初始化列表初始化的成员使⽤的
//• 尽量使⽤初始化列表初始化，因为那些你不在初始化列表初始化的成员也会⾛初始化列表，如果这个成员在声明位置给了缺省值，
//  初始化列表会⽤这个缺省值初始化。如果你没有给缺省值，对于没有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器
//  C++并没有规定。对于没有显⽰在初始化列表初始化的⾃定义类型成员会调⽤这个成员类型的默认构造函数，如果没有默认构造会编译错误。
//• 初始化列表中按照成员变量在类中声明顺序进⾏初始化，跟成员在初始化列表出现的的先后顺序⽆关。建议声明顺序和初始化列表顺序保持⼀致
//
// 每个成员都要走初始化列表
// 一.在初始化列表初始化的成员
// 二.没有在初始化列表的成员
//    1.声明的地方有缺省值用缺省值
//	  2.没有缺省值
//      a.内置类型：不稳定，看编译器，大概率是随机值
//		b.自定义类型：调用默认构造，没有默认构造就报错
// 三.引用const没有默认构造自定义，必须在初始化列表初始化
//class Time
//{
//public:
//	Time(int hour)
//		:_hour(hour)
//	{
//		cout << "Time()" << endl;
//	}
//private:
//	int _hour;
//};
//class Date
//{
//public:
//	Date(int& x, int year = 1, int month = 1, int day = 1)
//		:_year(year)
//		, _month(month)
//		, _day(day)
//		, _t(12)
//		, _ref(x)
//		, _n(1)
//	{
//		// error C2512: “Time”: 没有合适的默认构造函数可⽤
//		// error C2530 : “Date::_ref” : 必须初始化引⽤
//		// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象
//	}
//	void Print() const
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//	Time _t; // 没有默认构造
//	int& _ref; // 引⽤
//	const int _n; // const
//};
//int main()
//{
//	int i = 0;
//	Date d1(i);
//	d1.Print();
//	return 0;
//}

//class Time
//{
//public:
//	Time(int hour)
//		:_hour(hour)
//	{
//		cout << "Time()" << endl;
//	}
//private:
//	int _hour;
//};
//class Date
//{
//public:
//	Date()
//		:_month(2)
//	{
//		cout << "Date()" << endl;
//	}
//	void Print() const
//	{
//		cout << _year << "-" << _month << "-" << _day << endl;
//	}
//private:
//	// 注意这⾥不是初始化，这⾥给的是缺省值，这个缺省值是给初始化列表的
//	// 如果初始化列表没有显⽰初始化，默认就会⽤这个缺省值初始化
//	int _year = 1;
//	int _month = 1;
//	int _day;
//	Time _t = 1;
//	const int _n = 1;
//	int* _ptr = (int*)malloc(12);
//};
//int main()
//{
//	Date d1;
//	d1.Print();
//	return 0;
//}


//类型转换
//• C++⽀持内置类型隐式类型转换为类类型对象，需要有相关内置类型为参数的构造函数、
//• 构造函数前⾯加explicit就不再⽀持隐式类型转换
//class A
//{
//public:
//	// 构造函数explicit就不再⽀持隐式类型转换
//	// explicit A(int a1)
//	A(int a1)
//		:_a1(a1)
//	{}
//	//explicit A(int a1, int a2)
//	A(int a1, int a2)
//		:_a1(a1)
//		, _a2(a2)
//	{}
//	void Print()
//	{
//		cout << _a1 << " " << _a2 << endl;
//	}
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//int main()
//{
//	// 1构造⼀个A的临时对象，再⽤这个临时对象拷⻉构造aa3
//	// 编译器遇到连续构造+拷⻉构造->优化为直接构造
//	A aa1 = 1;
//	aa1.Print();
//
//	const A& aa2 = 1;
//	// C++11之后才⽀持多参数转化
//	A aa3 = { 2,2 };
//	return 0;
//}


// static成员
//• ⽤static修饰的成员变量，称之为静态成员变量，静态成员变量⼀定要在类外进⾏初始化。
//• 静态成员变量为所有类对象所共享，不属于某个具体的对象，不存在对象中，存放在静态区。
//• ⽤static修饰的成员函数，称之为静态成员函数，静态成员函数没有this指针。
//• 静态成员函数中可以访问其他的静态成员，但是不能访问⾮静态的，因为没有this指针。
//• ⾮静态的成员函数，可以访问任意的静态成员变量和静态成员函数。
//• 突破类域就可以访问静态成员，可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。
//• 静态成员也是类的成员，受public、protected、private 访问限定符的限制。
//• 静态成员变量不能在声明位置给缺省值初始化，因为缺省值是个构造函数初始化列表的，静态成员变量不属于某个对象，不⾛构造函数初始化列表。
//class A
//{
//public:
//	A()
//	{
//		++_scount;
//	}
//	A(const A& t)
//	{
//		++_scount;
//	}
//	~A()
//	{
//		--_scount;
//	}
//	static int GetACount()
//	{
//		return _scount;
//	}
//private:
//	// 类⾥⾯声明
//	static int _scount;
//};
//// 类外⾯初始化
//int A::_scount = 0;
//int main()
//{
//	cout << A::GetACount() << endl;
//	A a1, a2;
//	A a3(a1);
//	cout << A::GetACount() << endl;
//	cout << a1.GetACount() << endl;
//
//	// 编译报错：error C2248: “A::_scount”: ⽆法访问 private 成员(在“A”类中声明)
//	//cout << A::_scount << endl;
//	return 0;
//}


//友元
//• 友元提供了⼀种突破类访问限定符封装的⽅式，友元分为：友元函数和友元类，在函数声明或者类声明的前⾯加friend，并且把友元声明放到⼀个类的⾥⾯。
//• 外部友元函数可访问类的私有和保护成员，友元函数仅仅是⼀种声明，他不是类的成员函数。
//• 友元函数可以在类定义的任何地⽅声明，不受类访问限定符限制。
//• ⼀个函数可以是多个类的友元函数。
//• 友元类中的成员函数都可以是另⼀个类的友元函数，都可以访问另⼀个类中的私有和保护成员。
//• 友元类的关系是单向的，不具有交换性，⽐如A类是B类的友元，但是B类不是A类的友元。
//• 友元类关系不能传递，如果A是B的友元， B是C的友元，但是A不是B的友元。
//• 有时提供了便利。但是友元会增加耦合度，破坏了封装，所以友元不宜多⽤。
// 前置声明，都则A的友元函数声明编译器不认识B
//class B;
//class A
//{
//	// 友元声明
//	friend void func(const A& aa, const B& bb);
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//class B
//{
//	// 友元声明
//	friend void func(const A& aa, const B& bb);
//private:
//	int _b1 = 3;
//	int _b2 = 4;
//};
//void func(const A& aa, const B& bb)
//{
//	cout << aa._a1 << endl;
//	cout << bb._b1 << endl;
//}
//int main()
//{
//	A aa;
//	B bb;
//	func(aa, bb);
//	return 0;
//}

//class A
//{
//	// 友元声明
//	friend class B;
//private:
//	int _a1 = 1;
//	int _a2 = 2;
//};
//class B
//{
//public:
//	void func1(const A& aa)
//	{
//		cout << aa._a1 << endl;
//		cout << _b1 << endl;
//	}
//	void func2(const A& aa)
//	{
//		cout << aa._a2 << endl;
//		cout << _b2 << endl;
//	}
//private:
//	int _b1 = 3;
//	int _b2 = 4;
//};
//int main()
//{
//	A aa;
//	B bb;
//	bb.func1(aa);
//	bb.func1(aa);
//	return 0;
//}


//内部类
//• 如果⼀个类定义在另⼀个类的内部，这个内部类就叫做内部类。内部类是⼀个独⽴的类，跟定义在全局相⽐，
//	他只是受外部类类域限制和访问限定符限制，所以外部类定义的对象中不包含内部类。
//• 内部类默认是外部类的友元类。
//• 内部类本质也是⼀种封装，当A类跟B类紧密关联，A类实现出来主要就是给B类使⽤，那么可以考虑把A类设计为B的内部类，
//	如果放到private / protected位置，那么A类就是B类的专属内部类，其他地⽅都⽤不了。
//class A
//{
//private:
//	static int _k;
//	int _h = 1;
//public:
//	class B // B默认就是A的友元
//	{
//	public:
//		void foo(const A& a)
//		{
//			cout << _k << endl; //OK
//			cout << a._h << endl; //OK
//		}
//	};
//};
//int A::_k = 1;
//int main()
//{
//	cout << sizeof(A) << endl;
//	A::B b;
//	A aa;
//	b.foo(aa);
//	return 0;
//}


//匿名对象
//• ⽤ 类型(实参) 定义出来的对象叫做匿名对象，相⽐之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象
//• 匿名对象⽣命周期只在当前⼀⾏，⼀般临时定义⼀个对象当前⽤⼀下即可，就可以定义匿名对象。
//class A
//{
//public:
//	A(int a = 0)
//		:_a(a)
//	{
//		cout << "A(int a)" << endl;
//	}
//	~A()
//	{
//		cout << "~A()" << endl;
//	}
//private:
//	int _a;
//};
//class Solution {
//public:
//	int Sum_Solution(int n) {
//		//...
//		return n;
//	}
//};
//int main()
//{
//	A aa1;
//	// 不能这么定义对象，因为编译器⽆法识别下⾯是⼀个函数声明，还是对象定义
//	//A aa1();
//	// 但是我们可以这么定义匿名对象，匿名对象的特点不⽤取名字，
//	// 但是他的⽣命周期只有这⼀⾏，我们可以看到下⼀⾏他就会⾃动调⽤析构函数
//	A();
//	A(1);
//	A aa2(2);
//	// 匿名对象在这样场景下就很好⽤，当然还有⼀些其他使⽤场景，这个我们以后遇到了再说
//	Solution().Sum_Solution(10);
//	return 0;
//}


//对象拷⻉时的编译器优化
//• 现代编译器会为了尽可能提⾼程序的效率，在不影响正确性的情况下会尽可能减少⼀些传参和传返回值过程中可以省略的拷⻉。
//• 如何优化C++标准并没有严格规定，各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编译器对于
//	连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化，有些更新更"激进"的编译还会进⾏跨⾏跨表达式的合并优化。
//class A
//{
//public:
//	A(int a = 0)
//		:_a1(a)
//	{
//		cout << "A(int a)" << endl;
//	}
//	A(const A& aa)
//		:_a1(aa._a1)
//	{
//		cout << "A(const A& aa)" << endl;
//	}
//	A& operator=(const A& aa)
//	{
//		cout << "A& operator=(const A& aa)" << endl;
//		if (this != &aa)
//		{
//			_a1 = aa._a1;
//		}
//		return *this;
//	}
//	~A()
//	{
//		cout << "~A()" << endl;
//	}
//private:
//	int _a1 = 1;
//};
//void f1(A aa)
//{}
//A f2()
//{
//	A aa;
//	return aa;
//}
//int main()
//{
//	// 传值传参
//	A aa1;
//	f1(aa1);
//	cout << endl;
//	// 隐式类型，连续构造+拷⻉构造->优化为直接构造
//	f1(1);
//	// ⼀个表达式中，连续构造+拷⻉构造->优化为⼀个构造
//	f1(A(2));
//	cout << endl;
//	cout << "***********************************************" << endl;
//	// 传值返回
//	// 返回时⼀个表达式中，连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 （vs2019）
//	// ⼀些编译器会优化得更厉害，进⾏跨⾏合并优化，直接变为构造。（vs2022）
//	f2();
//	cout << endl;
//	// 返回时⼀个表达式中，连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 （vs2019）
//	// ⼀些编译器会优化得更厉害，进⾏跨⾏合并优化，直接变为构造。（vs2022）
//	A aa2 = f2();
//	cout << endl;
//	
//	// ⼀个表达式中，连续拷⻉构造+赋值重载->⽆法优化
//	aa1 = f2();
//	cout << endl;
//	
//	return 0;
//}

